﻿using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;

namespace Mongoose.Windows.Controls
{
    /// <summary>
    /// 
    /// </summary>
    public enum PadResizerPosition
    {
        Left,
        Top,
        Right,
        Bottom,
        TopLeft,
        TopRight,
        BottomLeft,
        BottomRight
    }

    /// <summary>
    /// A thumb dedicated to resize Pad Controls
    /// </summary>
    public class PadResizer : Thumb
    {
        /// <summary>
        /// Initializes the <see cref="PadResizer"/> class.
        /// </summary>
        static PadResizer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(PadResizer), new FrameworkPropertyMetadata(typeof(PadResizer)));
        }

        /// <summary>
        /// Gets or sets the position.
        /// </summary>
        /// <value>
        /// The position.
        /// </value>
        public PadResizerPosition Position
        {
            get { return (PadResizerPosition)GetValue(PositionProperty); }
            set { SetValue(PositionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Position.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PositionProperty =
            DependencyProperty.Register("Position", typeof(PadResizerPosition), typeof(PadResizer), new PropertyMetadata(PadResizerPosition.Left));

        /// <summary>
        /// When overridden in a derived class, is invoked whenever application code or internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate" />.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            DragStarted += OnDragStarted;
            DragDelta += OnDragDelta;
            DragCompleted += OnDragCompleted;
            
            switch (Position)
            {
                case PadResizerPosition.Left:
                case PadResizerPosition.Right:
                {
                    Cursor = Cursors.SizeWE;
                    break;
                }
                case PadResizerPosition.Top:
                case PadResizerPosition.Bottom:
                {
                    Cursor = Cursors.SizeNS;
                    break;
                }
                case PadResizerPosition.TopLeft:
                case PadResizerPosition.BottomRight:
                {
                    Cursor = Cursors.SizeNWSE;
                    break;
                }
                case PadResizerPosition.TopRight:
                case PadResizerPosition.BottomLeft:
                {
                    Cursor = Cursors.SizeNESW;
                    break;
                }
            }
        }

        /// <summary>
        /// Called when [drag started].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragStartedEventArgs"/> instance containing the event data.</param>
        void OnDragStarted(object sender, DragStartedEventArgs e)
        {
            if (Pad != null)
            {
                // Disable CacheMode during resizing
                Pad.CacheMode = null;
            }
        }

        /// <summary>
        /// Called when [drag delta].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        void OnDragDelta(object sender, DragDeltaEventArgs e)
        {
            if (Pad != null)
            {
                var currentPad = Pad;

                var isWidthChangeing = e.HorizontalChange != 0 && Position != PadResizerPosition.Top && Position != PadResizerPosition.Bottom;
                var isHeightChangeing = e.VerticalChange != 0 && Position != PadResizerPosition.Left && Position != PadResizerPosition.Right;

                if (isWidthChangeing)
                {
                    if (double.IsNaN(currentPad.Width))
                    {
                        currentPad.Width = currentPad.RenderSize.Width;
                    }

                    if (Position == PadResizerPosition.Left || Position == PadResizerPosition.BottomLeft || Position == PadResizerPosition.TopLeft)
                    {
                        var newWidth = currentPad.Width - e.HorizontalChange;
                        if (newWidth >= currentPad.MinWidth)
                        {
                            currentPad.Position = new Point(currentPad.Position.X + e.HorizontalChange, currentPad.Position.Y);
                            currentPad.Width -= e.HorizontalChange;
                        }
                    }
                    else
                    {
                        var newWidth = currentPad.Width + e.HorizontalChange;
                        if (newWidth >= currentPad.MinWidth)
                        {
                            currentPad.Width += e.HorizontalChange;
                        }
                    }
                }

                if (isHeightChangeing)
                {
                    if (double.IsNaN(currentPad.Height))
                    {
                        currentPad.Height = currentPad.RenderSize.Height;
                    }

                    if (Position == PadResizerPosition.Top || Position == PadResizerPosition.TopLeft || Position == PadResizerPosition.TopRight)
                    {
                        var newHeight = currentPad.Height - e.VerticalChange;
                        if (newHeight >= currentPad.MinHeight)
                        {
                            currentPad.Position = new Point(currentPad.Position.X, currentPad.Position.Y + e.VerticalChange);
                            currentPad.Height -= e.VerticalChange;
                        }
                    }
                    else
                    {
                        var newHeight = currentPad.Height + e.VerticalChange;
                        if (newHeight >= currentPad.MinHeight)
                        {
                            currentPad.Height += e.VerticalChange;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Called when [drag completed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragCompletedEventArgs"/> instance containing the event data.</param>
        void OnDragCompleted(object sender, DragCompletedEventArgs e)
        {
            if (Pad != null)
            {
                var surface = Pad.Container;
                if (surface != null && surface.HasDynamicLayout)
                {
                    surface.ArrangePads();
                }

                // reenable cache mode once resizing is over
                Pad.CacheMode = new BitmapCache(1) { EnableClearType = true, SnapsToDevicePixels = true };
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Controls.Control.MouseDoubleClick" /> routed event.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
        {
            base.OnMouseDoubleClick(e);

            var currentPad = Pad;
            if (currentPad != null)
            {
                switch (Position)
                {
                    case PadResizerPosition.Left:
                    case PadResizerPosition.Right:
                    {
                        currentPad.Width = double.NaN;
                        break;
                    }
                    case PadResizerPosition.Top:
                    case PadResizerPosition.Bottom:
                    {
                        currentPad.Height = double.NaN;
                        break;
                    }
                    case PadResizerPosition.TopLeft:
                    case PadResizerPosition.TopRight:
                    case PadResizerPosition.BottomLeft:
                    case PadResizerPosition.BottomRight:
                    {
                        currentPad.Width = double.NaN;
                        currentPad.Height = double.NaN;
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// Gets the pad.
        /// </summary>
        /// <value>
        /// The pad.
        /// </value>
        protected Pad Pad
        {
            get
            {
                if (pad == null)
                {
                    pad = this.SafeFindAncestor<Pad>();
                }
                return pad;
            }
        }

        #region private fields
        private Pad pad;
        #endregion
    }
}
